﻿'HIDEasy
'Copyright (C) 2011 ASFX

'This program is free software; you can redistribute it and/or
'modify it under the terms of the GNU General Public License
'as published by the Free Software Foundation; either version 2
'of the License, or (at your option) any later version.

'This program is distributed in the hope that it will be useful,
'but WITHOUT ANY WARRANTY; without even the implied warranty of
'MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
'GNU General Public License for more details.

'You should have received a copy of the GNU General Public License
'along with this program; if not, write to the Free Software
'Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
Imports System.ComponentModel

<CLSCompliant(False)> _
Public Class HIDItemCollection
    Implements IList
    Implements IList(Of HIDItem)
    Implements IDisposable

#Region "Events"
    Event HIDItemAdded As EventHandler(Of HIDItemCollectionAddedEventArgs)
    Event HIDItemRemoved As EventHandler(Of HIDItemCollectionRemovedEventArgs)
    Event HIDItemChanged As EventHandler(Of HIDItemCollectionChangedEventArgs)
#End Region

#Region "Variables"
    Private _ReportType As HIDReport.ReportType = Nothing
    Private _List As List(Of HIDItem) = New List(Of HIDItem)
    Private _Parent As HIDDevice = Nothing
    Private _LockObject As New Object
#End Region

#Region "Creator"
    Public Sub New(ByVal ReportType As HIDReport.ReportType)
        _ReportType = ReportType
    End Sub
#End Region
#Region "Properties"
    Public ReadOnly Property ReportType As HIDReport.ReportType
        Get
            Return _ReportType
        End Get
    End Property
#End Region
#Region "IList"

    Friend Overridable Sub Insert(ByVal index As Integer, _
                                  ByVal item As HIDItem) Implements IList(Of HIDItem).Insert
        SyncLock Me.SyncRoot
            _List.Insert(index, item)
        End SyncLock
        RaiseEvent HIDItemAdded(Me, New HIDItemCollectionAddedEventArgs(item))
    End Sub

    Default Public Property Item(ByVal index As Integer) As HIDItem Implements IList(Of HIDItem).Item
        Get
            Return _List(index)
        End Get
        Set(ByVal value As HIDItem)
            SyncLock Me.SyncRoot
                _List(index) = value
            End SyncLock
        End Set
    End Property

    Default Public Property Item(ByVal ReportID As Byte, _
                                 ByVal DataIndex As UInt32) As HIDItem
        Get
            SyncLock Me.SyncRoot
                For Each _item As HIDItem In Me
                    If _item.ReportID = ReportID And _item.DataIndex = DataIndex Then
                        Return _item
                    End If
                Next
                Return Nothing
            End SyncLock
        End Get
        Set(ByVal value As HIDItem)
            For Each _item As HIDItem In Me
                If _item.DataIndex = DataIndex Then
                    SyncLock Me.SyncRoot
                        _item = value
                    End SyncLock
                    Exit Property
                End If

            Next
            Throw _
                New HIDException( _
                                 "HIDItem with ReportID=" & ReportID.ToString & " and DataIndex=" & DataIndex.ToString _
                                 & " not found.")
        End Set
    End Property

    Friend Shadows Sub RemoveAt(ByVal index As Integer) Implements IList(Of HIDItem).RemoveAt
        Dim _item As HIDItem = Me.Item(index)
        SyncLock Me.SyncRoot
            _List.RemoveAt(index)
        End SyncLock
        RaiseEvent HIDItemRemoved(Me, New HIDItemCollectionRemovedEventArgs(_item))
    End Sub

    Public Function IndexOf(ByVal item As HIDItem) As Integer Implements IList(Of HIDItem).IndexOf
        Return _List.IndexOf(item)
    End Function

    Public Function IndexOf(ByVal ReportID As Byte, _
                            ByVal DataIndex As UInt32) As Integer
        SyncLock Me.SyncRoot
            For Each item As HIDItem In _List
                If item.ReportID = ReportID And item.DataIndex = DataIndex Then Return _List.IndexOf(item)
            Next
        End SyncLock
        'not found
        Return -1

    End Function

#End Region

#Region "ICollection"

    Friend Sub Add(ByVal item As HIDItem) Implements ICollection(Of HIDItem).Add
        SyncLock Me.SyncRoot
            If Me.Contains(item) Or Me.Contains(item.ReportID, item.DataIndex) Then
                Throw New NotSupportedException("Unable to add HIDItem (ReportID=" & item.ReportID.ToString & ", " _
                                                & "DataIndex=" & item.DataIndex.ToString _
                                                & ") item already exits is this collection.")
            ElseIf item.Parent IsNot Nothing Then
                Throw New NotSupportedException("Unable to add HIDItem (ReportID=" & item.ReportID.ToString & ", " _
                                                & "DataIndex=" & item.DataIndex.ToString _
                                                & ") item already exits in an another collection.")
            Else
                ' Order the list by DataIndex
                Dim _inserted As Boolean = False
                For i As Integer = 0 To _List.Count - 1
                    If _List(i).DataIndex > item.DataIndex Then
                        _List.Insert(i, item)
                        _inserted = True
                        Exit For
                    End If
                Next
                If Not _inserted Then
                    ' Insert Item at last position
                    _List.Insert(_List.Count, item)
                End If

                item.Parent = Me
                AddHandler item.Changed, AddressOf OnHIDItemChanged
                RaiseEvent HIDItemAdded(Me, New HIDItemCollectionAddedEventArgs(item))
            End If
        End SyncLock
    End Sub

    Friend Sub AddRange(ByVal list As IEnumerable(Of HIDItem))
        SyncLock Me.SyncRoot
            For Each _itm As HIDItem In list
                If Me.Contains(_itm) Or Me.Contains(_itm.ReportID, _itm.DataIndex) Then
                    Throw _
                        New NotSupportedException( _
                                                  "Unable to HIDItem Range, HIDItem (ReportID=" & _itm.ReportID.ToString _
                                                  & ", " _
                                                  & "DataIndex=" & _itm.DataIndex.ToString _
                                                  & ") item already exits is this collection.")
                ElseIf _itm.Parent IsNot Nothing Then
                    Throw _
                        New NotSupportedException( _
                                                  "Unable to add HIDItem Range, HIDItem (ReportID=" _
                                                  & _itm.ReportID.ToString & ", " _
                                                  & "DataIndex=" & _itm.DataIndex.ToString _
                                                  & ") item already exits in an another collection.")
                End If
            Next
            For Each _itm As HIDItem In list
                Me.Add(_itm)
            Next
        End SyncLock
    End Sub

    Friend Function Remove(ByVal item As HIDItem) As Boolean Implements ICollection(Of HIDItem).Remove
        SyncLock Me.SyncRoot
            If Me.Contains(item) Then
                _List.Remove(item)
                RemoveHandler item.Changed, AddressOf OnHIDItemChanged
                item.Parent = Nothing
            Else
                Return False
            End If
        End SyncLock
        If item Is Nothing Then
            Return False
        Else
            RaiseEvent HIDItemRemoved(Me, New HIDItemCollectionRemovedEventArgs(item))
            Return True
        End If
    End Function

    Friend Function Remove(ByVal ReportID As Byte, _
                           ByVal DataIndex As UShort) As Boolean
        Dim _item As HIDItem = Nothing
        Dim _result As Boolean = False
        SyncLock Me.SyncRoot
            If Me.Contains(ReportID, DataIndex) Then
                _item = Me.Item(ReportID, DataIndex)
                _result = _List.Remove(_item)
                If Not _result Then Return False
                RemoveHandler _item.Changed, AddressOf OnHIDItemChanged
                _item.Parent = Nothing
            End If
        End SyncLock
        If _result Then
            If _item IsNot Nothing Then RaiseEvent HIDItemRemoved(Me, New HIDItemCollectionRemovedEventArgs(_item))
        End If
        Return _result
    End Function

    Friend Shadows Sub Clear() Implements ICollection(Of HIDItem).Clear
        Do While Me.Count > 0
            Dim _HIDItem As HIDItem = Me.Item(0)
            Me.RemoveAt(0)
            _HIDItem.Dispose()
        Loop
        _List.Clear()
    End Sub

    Public Function Contains(ByVal item As HIDItem) As Boolean Implements ICollection(Of HIDItem).Contains
        Return _List.Contains(item)
    End Function

    Public Function Contains(ByVal ReportID As Byte, _
                             ByVal DataIndex As UShort) As Boolean
        SyncLock Me.SyncRoot
            For Each _item As HIDItem In Me
                If _item.ReportID = ReportID And _item.DataIndex = DataIndex Then
                    Return True
                End If
            Next
            Return False
        End SyncLock
    End Function

    Public Overridable Sub CopyTo(ByVal array() As HIDItem, _
                                  ByVal arrayIndex As Integer) Implements ICollection(Of HIDItem).CopyTo
        SyncLock Me.SyncRoot
            _List.CopyTo(array, arrayIndex)
        End SyncLock
    End Sub

    <Category("Items")> _
    Public Shadows ReadOnly Property Count() As Integer Implements ICollection(Of HIDItem).Count
        Get
            Return _List.Count
        End Get
    End Property

    Public ReadOnly Property IsReadOnly() As Boolean Implements ICollection(Of HIDItem).IsReadOnly
        Get
            Return True
        End Get
    End Property

#End Region

#Region "IEnumerator"

    Public Function GetEnumerator() As IEnumerator(Of HIDItem) Implements IEnumerable(Of HIDItem).GetEnumerator
        Return New HIDItemEnumerator(_List.GetEnumerator)
    End Function

    Public Function GetEnumerator1() As IEnumerator Implements IEnumerable.GetEnumerator
        Return New HIDItemEnumerator(_List.GetEnumerator)
    End Function

#End Region

    Public Overrides Function ToString() As String
        Select Case Me.Count
            Case 0
                Return "No Item"
            Case 1
                Return "1 Item"
            Case Else
                Return Me.Count.ToString & " Items"
        End Select
    End Function

#Region "On HIDItem Changed"

    Protected Sub OnHIDItemChanged(ByVal sender As Object, _
                                   ByVal e As HIDItemChangedEventArgs)
        Dim _ListRadioButtonGroup_SetButtonToZero As List(Of HIDItem) = New List(Of HIDItem)
        SyncLock Me.SyncRoot
            If e.ItemChanged.IsButton _
               AndAlso e.ItemChanged.IsRadioButton _
               AndAlso e.ItemChanged.LogicalValue <> 0 Then
                For Each _item As HIDItem In GetButtons(Me, e.ItemChanged.ReportID, e.ItemChanged.RadioButtonGroup)
                    If e.ItemChanged.DataIndex <> _item.DataIndex Then
                        If e.ItemChanged.ValueExists Then
                            If e.ItemChanged.LogicalValue <> 0 Then
                                _ListRadioButtonGroup_SetButtonToZero.Add(_item)
                            End If
                        End If
                    End If
                Next
            End If
        End SyncLock
        For Each _item As HIDItem In _ListRadioButtonGroup_SetButtonToZero
            _item.SetLogicalValueMin(False)
            RaiseEvent HIDItemChanged(Me, New HIDItemCollectionChangedEventArgs(_item))
            '_item.HasChanged = False
        Next
        RaiseEvent HIDItemChanged(Me, New HIDItemCollectionChangedEventArgs(e.ItemChanged))
        'e.ItemChanged.HasChanged = False
    End Sub

#End Region

#Region "IList"

    Private ReadOnly Property IList_Count() As Integer Implements ICollection.Count
        Get
            Return _List.Count
        End Get
    End Property

    Private Sub IList_CopyTo(ByVal array As Array, _
                             ByVal index As Integer) Implements ICollection.CopyTo
        Me.CopyTo(CType(array, HIDItem()), index)
    End Sub

    Public ReadOnly Property IsSynchronized() As Boolean Implements ICollection.IsSynchronized
        Get
            Return True
        End Get
    End Property

    Public ReadOnly Property SyncRoot() As Object Implements ICollection.SyncRoot
        Get
            Return _LockObject
        End Get
    End Property

    Private Function IList_Add(ByVal value As Object) As Integer Implements IList.Add
        SyncLock Me.SyncRoot
            Me.Add(CType(value, HIDItem))
            Return IndexOf(CType(value, HIDItem))
        End SyncLock
    End Function

    Private Sub IList_Clear() Implements IList.Clear
        Me.Clear()
    End Sub

    Private Function IList_Contains(ByVal value As Object) As Boolean Implements IList.Contains
        Return Me.Contains(CType(value, HIDItem))
    End Function

    Private Function IList_IndexOf(ByVal value As Object) As Integer Implements IList.IndexOf
        Return Me.IndexOf(CType(value, HIDItem))
    End Function

    Private Sub IList_Insert(ByVal index As Integer, _
                             ByVal value As Object) Implements IList.Insert
        Me.Insert(index, CType(value, HIDItem))
    End Sub

    Public ReadOnly Property IList_IsFixedSize() As Boolean Implements IList.IsFixedSize
        Get
            Return False
        End Get
    End Property

    Private ReadOnly Property IList_IsReadOnly() As Boolean Implements IList.IsReadOnly
        Get
            Return Me.IsReadOnly
        End Get
    End Property

    Private Property IList_Item(ByVal index As Integer) As Object Implements IList.Item
        Get
            Return CType(Me.Item(index), HIDItem)
        End Get
        Set(ByVal value As Object)
            SyncLock Me.SyncRoot
                Me.Item(index) = CType(value, HIDItem)
            End SyncLock
        End Set
    End Property

    Private Sub IList_Remove(ByVal value As Object) Implements IList.Remove
        Me.Remove(CType(value, HIDItem))
    End Sub

    Private Sub IList_RemoveAt(ByVal index As Integer) Implements IList.RemoveAt
        Me.RemoveAt(index)
    End Sub

#End Region

#Region "Class Enumerator"

    Private Class HIDItemEnumerator
        Implements IEnumerator(Of HIDItem)
        Private _Enumerator As IEnumerator

        Public Sub New(ByVal enumerator As IEnumerator)
            _Enumerator = enumerator
        End Sub

        Public ReadOnly Property Current() As HIDItem Implements IEnumerator(Of HIDItem).Current
            Get
                Return CType(_Enumerator.Current, HIDItem)
            End Get
        End Property

        Private ReadOnly Property Current1() As Object Implements IEnumerator.Current
            Get
                Return Me.Current
            End Get
        End Property

        Public Function MoveNext() As Boolean Implements IEnumerator.MoveNext
            Return _Enumerator.MoveNext
        End Function

        Public Sub Reset() Implements IEnumerator.Reset
            _Enumerator.Reset()
        End Sub

#Region " IDisposable Support "
        Private disposedValue As Boolean = False
        ' To detect redundant calls

        ' IDisposable
        Protected Overridable Sub Dispose(ByVal disposing As Boolean)
            If Not Me.disposedValue Then
                If disposing Then

                End If
            End If
            Me.disposedValue = True
        End Sub

        Public Sub Dispose() Implements IDisposable.Dispose
            Dispose(True)
            GC.SuppressFinalize(Me)
        End Sub

#End Region
    End Class

#End Region

#Region "Shared Functions"

    Public Shared Function GetItems(ByVal collection As IList(Of HIDItem), _
                                    ByVal ReportId As Byte) As List(Of HIDItem)
        Dim _list As List(Of HIDItem) = New List(Of HIDItem)
        For Each _item As HIDItem In collection
            If _item.ReportID = ReportId Then
                _list.Add(_item)
            End If
        Next
        Return _list
    End Function

    Public Shared Function GetItems(ByVal collection As IList(Of HIDItem), _
                                    ByVal LinkUsagePage As UInt16, _
                                    ByVal UsagePage As UInt16) As List(Of HIDItem)
        Dim _list As List(Of HIDItem) = New List(Of HIDItem)
        For Each _item As HIDItem In collection
            If _item.LinkUsagePage = LinkUsagePage _
               AndAlso _item.UsagePage = UsagePage Then
                _list.Add(_item)
            End If
        Next
        Return _list
    End Function

    Public Shared Function GetItems(ByVal collection As IList(Of HIDItem), _
                                    ByVal ReportId As Byte, _
                                    ByVal LinkUsagePage As UInt16, _
                                    ByVal UsagePage As UInt16) As List(Of HIDItem)
        Dim _list As List(Of HIDItem) = New List(Of HIDItem)
        For Each _item As HIDItem In collection
            If _item.ReportID = ReportId _
               AndAlso _item.LinkUsagePage = LinkUsagePage _
               AndAlso _item.UsagePage = UsagePage Then
                _list.Add(_item)
            End If
        Next
        Return _list
    End Function

    Public Shared Function GetReportIDs(ByVal collection As IList(Of HIDItem)) As List(Of Byte)
        Dim _list As List(Of Byte) = New List(Of Byte)
        For Each _item As HIDItem In collection
            If Not _list.Contains(_item.ReportID) Then
                _list.Add(_item.ReportID)
            End If
        Next
        Return _list
    End Function

    Public Shared Function GetButtons(ByVal collection As IList(Of HIDItem), _
                                      ByVal ReportId As Byte) As List(Of HIDItem)
        Dim _list As List(Of HIDItem) = New List(Of HIDItem)
        For Each _item As HIDItem In collection
            If _item.IsButton _
               AndAlso _item.ReportID = ReportId Then
                _list.Add(_item)
            End If
        Next
        Return _list
    End Function

    Public Shared Function GetButtons(ByVal collection As IList(Of HIDItem), _
                                      ByVal LinkUsagePage As UInt16, _
                                      ByVal UsagePage As UInt16) As List(Of HIDItem)
        Dim _list As List(Of HIDItem) = New List(Of HIDItem)
        For Each _item As HIDItem In collection
            If _item.IsButton _
               AndAlso _item.LinkUsagePage = LinkUsagePage _
               AndAlso _item.UsagePage = UsagePage Then
                _list.Add(_item)
            End If
        Next
        Return _list
    End Function

    Public Shared Function GetButtons(ByVal collection As IList(Of HIDItem), _
                                      ByVal ReportId As Byte, _
                                      ByVal LinkCollection As UInt16, _
                                      ByVal LinkUsagePage As UInt16, _
                                      ByVal UsagePage As UInt16) As List(Of HIDItem)
        Dim _list As List(Of HIDItem) = New List(Of HIDItem)
        For Each _item As HIDItem In collection
            If _item.IsButton _
               AndAlso _item.ReportID = ReportId _
               AndAlso _item.LinkCollection = LinkCollection _
               AndAlso _item.LinkUsagePage = LinkUsagePage _
               AndAlso _item.UsagePage = UsagePage Then
                _list.Add(_item)
            End If
        Next
        Return _list
    End Function

    Public Shared Function GetButtons(ByVal collection As IList(Of HIDItem), _
                                      ByVal ReportId As Byte, _
                                      ByVal RadioButtonGroup As UInt32) As List(Of HIDItem)
        Dim _list As List(Of HIDItem) = New List(Of HIDItem)
        For Each _item As HIDItem In collection
            If _item.IsButton _
               AndAlso _item.ReportID = ReportId _
               AndAlso _item.IsRadioButton _
               AndAlso _item.RadioButtonGroup = RadioButtonGroup Then
                _list.Add(_item)
            End If
        Next
        Return _list
    End Function

#End Region

#Region " IDisposable Support "
    Private disposedValue As Boolean = False
    ' To detect redundant calls

    ' IDisposable
    Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        If Not Me.disposedValue Then
            If disposing Then
                _List.Clear()
                _List = Nothing
            End If

        End If
        Me.disposedValue = True
    End Sub

    Friend Sub Dispose() Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub

#End Region
End Class

